package io.netty.util.concurrent;

import io.netty.util.internal.ObjectUtil;

import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

/**
 * Expose helper methods which create different {@link RejectedExecutionHandler}s.
 */
public final class RejectedExecutionHandlers {

    private static final RejectedExecutionHandler REJECT = (task,executor) -> {
        throw new RejectedExecutionException();
    };

    private RejectedExecutionHandlers() {

    }

    /**
     * Returns a {@link RejectedExecutionHandler} that will always just throw a {@link RejectedExecutionException}.
     */
    public static RejectedExecutionHandler reject() {
        return REJECT;
    }

    /**
     * Tries to backoff when the task can not be added due restrictions for an configured amount of time. This
     * is only done if the task was added from outside of the event loop which means
     * {@link EventExecutor#inEventLoop()} returns {@code false}.
     */
    public static RejectedExecutionHandler backOff(final int retries, long backOffAmount, TimeUnit unit) {
        ObjectUtil.checkPositive(retries,"retries");
        final long backOffNanos = unit.toNanos(backOffAmount);
        return (task, executor) -> {
            if (!executor.inEventLoop()) {
                for (int i = 0;i < retries;i ++) {
                    // Try to wake up the executor so it will empty its task queue.
                    executor.wakeup(false);

                    LockSupport.parkNanos(backOffNanos);

                    if (executor.offerTask(task)) {
                        return;
                    }
                }
                // Either we tried to add the task from within the EventLoop or we was not able to add it even with
                // backoff.
                throw new RejectedExecutionException();
            }
        };
    }
}
